package multithread;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;

/**
 * This class is an implementation of ThreadFactory which 
 * is based in a thread pool. This pool has a limit size, and
 * the factory only creates another thread when the pool is not 
 * full.
 */
public class PrimeThreadFactory implements ThreadFactory {
	
	private ArrayBlockingQueue<Thread> runningPool;
	
	public PrimeThreadFactory(int poolSize) {
		runningPool = new ArrayBlockingQueue<Thread>(poolSize);
	}
	
	@Override
	public Thread newThread(Runnable r) {
		Thread toReturn = new Thread(new FinishableRunnable(this, r));
		try {
			runningPool.put(toReturn);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return toReturn;
	}
	
	/**
	 * A special Runnable that can report the end of execution to the 
	 * factory.
	 */
	private class FinishableRunnable implements Runnable {

		private PrimeThreadFactory f;
		private Runnable r;
		
		public FinishableRunnable(PrimeThreadFactory f, Runnable r) {
			this.f = f;
			this.r = r;
		}
		
		@Override
		public void run() {
			/* process */
			r.run();
			/* report the end */
			f.finished();
		} 
	}
	
	/**
	 * The FinishableRunnables call this method when the run method of 
	 * the original Runnable returns. It's used to remove one thread from 
	 * the queue and allow other thread to be created.
	 */
	private void finished() {
		/* Remove one thread from the queue */
		runningPool.poll();
		try {
			Thread.currentThread().join(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
